import { MessageEvent } from 'message-event';

/**
 * 队列参数
 * queue: 队列列表
 * id: 队列唯一id
 * result: 队列运行后的结果
 */
type IQueues<F = Array<Function>> = {
	queue: F;
	id?: string;
	result?: unknown[];
};

// 系统内部队列
type IQueueList = IQueues<Function>[];

// 参数传入的队列结构
type IQueuesParams = Function[];

// 队列状态
enum IState {
	// 空闲
	idle = 'idle',
	// 等待/运行中
	pending = 'pending',
	// 完成
	fulfilled = 'fulfilled',
	// 失败
	rejected = 'rejected',
}

// 队列监听类型
enum IEventType {
	// 等待/运行中
	pending = 'pending',
	// 结束
	finish = 'finish',
}

// 队列缓存结构
type IQueueCacheTemp = {
	[key: string]: IQueues;
};

// 任务队列参数
type ITaskQueueProps = {
	maxLen: number;
};

// 展开promise
type IDefer = {
	resolve: Function;
	reject: Function;
	promise: Promise<any>;
};

const defer = (): IDefer => {
	// @ts-ignore
	const res: IDefer = {};
	const promise = new Promise((resolve, reject) => {
		res.resolve = resolve;
		res.reject = reject;
	});
	// @ts-ignore
	res.promise = promise;
	return res;
};

/**
 * 任务队列构造器
 */
class TaskQueue {
	props: ITaskQueueProps;
	queues: IQueueList = [];
	queueTemp: IQueueCacheTemp = {};
	state: IState = IState.idle;
	defaultKey: string = 'default';
	defaultLen: number = 10;
	messageEvent: MessageEvent = new MessageEvent(); // 消息
	constructor(props: ITaskQueueProps) {
		this.props = props;
	}

	/**
	 * 添加任务
	 */
	push(queues: IQueuesParams, id: string = this.defaultKey) {
		this.checkQueue(queues, id);
		const { resolve, reject, promise } = defer();
		try {
			this.setQueue(queues, id); // 把传入的列表转为内部数据结构
			this.run();
			this.messageEvent.on(id, resolve); // 监听当前任务id的结束事件
		} catch (erorr) {
			this.state = IState.rejected;
			reject();
		}
		return promise;
	}

	/**
	 * 监听事件
	 * @param type
	 * @param handler
	 */
	on(type: IEventType, handler: Function) {
		if (!type || !handler) {
			throw new Error(
				'The type of the listening event and the handler function must be passed.',
			);
		}
		if (typeof handler !== 'function') {
			throw new Error('The second argument should be a function.');
		}

		const handlerFn = (...args: any[]) => {
			handler(...args);
		};
		this.messageEvent.on(type, handlerFn);
	}

	/**
	 * 执行任务
	 */
	private async run() {
		while (this.queues.length > 0 && this.state !== IState.pending) {
			this.state = IState.pending;
			const queues = this.getQueues(this.props.maxLen);
			this.messageEvent.emit(IEventType.pending, queues); // 发布pending事件
			const result = await Promise.all(
				queues.map((itm) => {
					const res = itm.queue();
					return res;
				}),
			);
			this.state = IState.fulfilled;
			this.finish({ result, queues });
		}
	}

	/**
	 * 任务结束后回调
	 */
	private finish({
		result = [],
		queues = [],
	}: {
		result: any[];
		queues: any[];
	}) {
		const { queueTemp } = this;
		// 在map中同步结果
		queues.forEach((item: IQueues<Function>, i) => {
			const key = item.id as string;
			const temp = queueTemp[key];
			if (temp) {
				temp.result?.push(result[i]);
				// 如果结果和任务数量一样，则为结束
				if (temp.result?.length === temp.queue?.length) {
					this.messageEvent.emit(item.id as string, temp.result); // 发布当前任务执行完的事件
					this.messageEvent.off(item.id as string); // 移出监听事件
					delete queueTemp[key];
					this.state = IState.idle;
					// 发布结束事件
					this.messageEvent.emit(IEventType.finish, {
						id: key,
						result: temp.result,
					});
				}
			}
		});
	}

	/**
	 * 重置整个队列
	 */
	reset() {
		this.state = IState.idle;
		this.queues = [];
		this.queueTemp = {};
		this.messageEvent.clear(); // 清空事件监听
		console.log(this);
	}

	private _isPromise(p: any) {
		return p instanceof Promise;
	}

	/**
	 * 获取需要执行的队列
	 * @param size
	 * @returns
	 */
	private getQueues(size?: number): IQueueList {
		return this.queues.splice(0, size);
	}

	/**
	 * 设置队列
	 * @param queues
	 * @param id
	 */
	private setQueue(queues: IQueuesParams, id: string) {
		const { queueTemp } = this;
		const __queueMap = { id: id, result: [] };
		const item = queueTemp[id] || { queue: [], ...__queueMap };
		const tempQueue = item?.queue;
		queues.forEach((queueFn: Function) => {
			tempQueue.push(queueFn);
			// 放入到队列集合中
			this.queues.push({
				queue: queueFn,
				...__queueMap,
			});
		});
		// 映射到缓存map中
		queueTemp[id] = item;
	}

	/**
	 * 检查队列
	 * @param queue
	 * @param id
	 */
	private checkQueue(queues: IQueuesParams, id: string) {
		if (!queues) {
			throw new Error('queues cannot be empty.');
		}
		if (!Array.isArray(queues)) {
			throw new Error('The queues should be an array.');
		}

		if (queues.length === 0) {
			throw new Error('queues.length can not be 0');
		}

		const notFn = (f: any) => !f || typeof f !== 'function';

		if (queues.find(notFn)) {
			throw new Error('The item in the queue list should be a function.');
		}
	}
}

export { TaskQueue };
